package com.qqbot.utils;

import com.qqbot.enums.DateIntervalTypeEnum;
import com.qqbot.enums.DatePattern;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 日期工具类
 */
public class DateUtil {
    public static final int CEIL = 1;
    public static final int FLOOR = 2;
    public static final int ROUND = 3;
    private static final Map<Integer, Integer> DAY_OF_WEEK_TO_CHINESE_NUMBER = new HashMap<Integer, Integer>() {{
        put(Calendar.MONDAY, 1);
        put(Calendar.TUESDAY, 2);
        put(Calendar.WEDNESDAY, 3);
        put(Calendar.THURSDAY, 4);
        put(Calendar.FRIDAY, 5);
        put(Calendar.SATURDAY, 6);
        put(Calendar.SUNDAY, 7);
    }};

    public static final int MONTH_WEIGHT_OF_SEASON = 3;//季度的月份系数
    public static final int MONTH_WEIGHT_OF_HALF_YEAR = 6;//半年的月份系数
    public static final int MAX_VALUE_OF_SEASON = 4;//季度域最大值
    public static final int MAX_VALUE_OF_HALF_YEAR = 2;//半年域最大值

    private static SimpleDateFormat getDateFormat(String parttern) throws RuntimeException {
        return new SimpleDateFormat(parttern);
    }

    /**
     * 初始化日历
     *
     * @param date 日期
     * @return 日历对象
     */
    public static Calendar initCalendar(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(date);
        return calendar;
    }

    /**
     * 修改日期的域值
     *
     * @param date      日期，默认值为当前日期
     * @param dateField 日期域
     * @param amount    变更值
     * @return 修改后的日期
     */
    public static Date addField(Date date, DateField dateField, Integer amount) {
        if (null == date) {
            date = new Date();
        }
        if (DateField.SEASON.equals(dateField)) {
            dateField = DateField.MONTH;
            amount = MONTH_WEIGHT_OF_SEASON * amount;
        }
        if (DateField.HALF_YEAR.equals(dateField)) {
            dateField = DateField.MONTH;
            amount = MONTH_WEIGHT_OF_HALF_YEAR * amount;
        }
        Calendar calendar = initCalendar(date);
        calendar.add(dateField.getCode(), amount);
        return calendar.getTime();
    }

    /**
     * 获取日期的域值
     *
     * @param date      日期，默认值为当前日期
     * @param dateField 日期域
     * @return 域值
     */
    public static int readField(Date date, DateField dateField) {
        if (null == date) {
            date = new Date();
        }
        if (DateField.SEASON.equals(dateField)) {
            int month = readField(date, DateField.MONTH);
            return (int) Math.ceil(month * 1.0 / MONTH_WEIGHT_OF_SEASON);
        }
        if (DateField.HALF_YEAR.equals(dateField)) {
            int month = readField(date, DateField.MONTH);
            return (int) Math.ceil(month * 1.0 / MONTH_WEIGHT_OF_HALF_YEAR);
        }
        Calendar calendar = initCalendar(date);
        int fieldValue = calendar.get(dateField.getCode());
        if (DateField.MONTH.equals(dateField)) {
            return fieldValue + 1;
        }
        if (DateField.DAY_OF_WEEK.equals(dateField)) {
            return DAY_OF_WEEK_TO_CHINESE_NUMBER.get(fieldValue);
        }
        return fieldValue;
    }

    /**
     * 构建日期
     *
     * @param year  年
     * @param month 月，中国习惯月份
     * @param date  日
     * @return
     */
    public static Date buildDate(int year, int month, int date) {
        return buildDate(year, month, date, 0, 0, 0);
    }

    /**
     * 构建日期
     *
     * @param year      年
     * @param month     月，中国习惯月份
     * @param date      日
     * @param hourOfDay 小时，二十四小时制
     * @param minute    分钟
     * @param second    秒
     * @return
     */
    public static Date buildDate(int year, int month, int date, int hourOfDay, int minute, int second) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(year, month - 1, date, hourOfDay, minute, second);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 构建日期
     *
     * @param year        年
     * @param month       月，中国习惯月份
     * @param date        日
     * @param hourOfDay   小时，二十四小时制
     * @param minute      分钟
     * @param second      秒
     * @param millisecond 毫秒
     * @return
     */
    public static Date buildDate(int year, int month, int date, int hourOfDay, int minute, int second, int millisecond) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(year, month - 1, date, hourOfDay, minute, second);
        calendar.set(Calendar.MILLISECOND, millisecond);
        return calendar.getTime();
    }

    /**
     * 计算时间差距
     *
     * @param date1            日期1
     * @param date2            日期2
     * @param timeIntervalType 计算时间维度：1：秒、2：分、3：小时、4：天
     * @param valueType        返回值维度：1：向上取整、2：向下取整、3：四舍五入
     * @return
     */
    public static Long getTimeInterval(Date date1, Date date2, DateIntervalTypeEnum timeIntervalType, int valueType) {
        Long result = 0L;
        if (date1 == null || date2 == null) {
            return result;
        }
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(date1);
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date2);
        long milliSecond = Math.abs(calendar1.getTimeInMillis() - calendar2.getTimeInMillis());
        DateIntervalTypeEnum dateIntervalTypeEnum = DateIntervalTypeEnum.find(timeIntervalType.getCode());
        double value = milliSecond * 1.0 / dateIntervalTypeEnum.getValue() * 1.0;
        if (CEIL == valueType) {//向上取整
            result = (long) Math.ceil(value);
        } else if (FLOOR == valueType) {//向下取整
            result = (long) Math.floor(value);
        } else {//四舍五入
            result = Math.round(value);
        }
        return result;
    }

    /**
     * 创建指定日期的开始时间
     *
     * @param originDate 指定日期
     * @return 指定日期的开始时间
     */
    public static Date buildDateStart(Date originDate) {
        int year = readField(originDate, DateField.YEAR);
        int month = readField(originDate, DateField.MONTH);
        int date = readField(originDate, DateField.DATE);
        return buildDate(year, month, date);
    }

    /**
     * 创建指定日期的结束时间
     *
     * @param originDate 指定日期
     * @return 指定日期的结束时间
     */
    public static Date buildDateEnd(Date originDate) {
        int year = readField(originDate, DateField.YEAR);
        int month = readField(originDate, DateField.MONTH);
        int date = readField(originDate, DateField.DATE);
        return buildDate(year, month, date, 23, 59, 59, 999);
    }

    /**
     * 创建指定日期的起始时间
     *
     * @param originDate 指定日期
     * @return 所在日期的起始时间
     */
    public static DateRange buildDateRange(Date originDate) {
        int year = readField(originDate, DateField.YEAR);
        int month = readField(originDate, DateField.MONTH);
        int date = readField(originDate, DateField.DATE);
        Date startOfDate = buildDate(year, month, date);
        Date endOfDate = buildDate(year, month, date, 23, 59, 59, 999);
        return new DateRange(startOfDate, endOfDate);
    }

    /**
     * 创建指定日期所在周的起始时间
     *
     * @param originDate 指定日期
     * @return 所在周的起始时间
     */
    public static DateRange buildWeekRange(Date originDate) {
        Calendar calendar = initCalendar(originDate);
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        Date weekStart = buildDateStart(calendar.getTime());
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        Date weekEnd = buildDateEnd(calendar.getTime());
        return new DateRange(weekStart, weekEnd);
    }

    /**
     * 创建指定日期所在月份的开始时间
     *
     * @param originDate 指定日期
     * @return 所在月份的开始时间
     */
    public static Date buildMonthStart(Date originDate) {
        int year = readField(originDate, DateField.YEAR);
        int month = readField(originDate, DateField.MONTH);
        return buildDate(year, month, 1);
    }

    /**
     * 创建指定日期所在月份的结束时间
     *
     * @param originDate 指定日期
     * @return 所在月份的结束时间
     */
    public static Date buildMonthEnd(Date originDate) {
        int year = readField(originDate, DateField.YEAR);
        int month = readField(originDate, DateField.MONTH);
        int date = getActualMaxDaysOfMonth(originDate);
        return buildDate(year, month, date, 23, 59, 59, 999);
    }

    /**
     * 创建指定日期所在月份的起始时间
     *
     * @param originDate 指定日期
     * @return 所在月份的起始时间
     */
    public static DateRange buildMonthRange(Date originDate) {
        int year = readField(originDate, DateField.YEAR);
        int month = readField(originDate, DateField.MONTH);
        int date = getActualMaxDaysOfMonth(originDate);
        Date startOfMonth = buildDate(year, month, 1);
        Date endOfMonth = buildDate(year, month, date, 23, 59, 59, 999);
        return new DateRange(startOfMonth, endOfMonth);
    }

    /**
     * 创建指定日期所在季度的起始时间
     *
     * @param originDate 指定日期
     * @return 所在季度的起始时间
     */
    public static DateRange buildSeasonRange(Date originDate) {
        int month = DateUtil.readField(originDate, DateField.MONTH);
        int interval;
        if (month % MONTH_WEIGHT_OF_SEASON == 0) {
            interval = 1 - MONTH_WEIGHT_OF_SEASON;
        } else if (month % MONTH_WEIGHT_OF_SEASON == 1) {
            interval = 0;
        } else {
            interval = month % MONTH_WEIGHT_OF_SEASON - MONTH_WEIGHT_OF_SEASON;
        }
        Date startOfLastSeason = DateUtil.buildMonthStart(DateUtil.addField(originDate, DateField.MONTH, interval));
        Date endOfOfLastSeason = DateUtil.buildMonthEnd(DateUtil.addField(startOfLastSeason, DateField.MONTH, 2));
        return new DateRange(startOfLastSeason, endOfOfLastSeason);
    }

    /**
     * 获取日期所在月度，最大的实际天数
     *
     * @param date 日期，默认值：当前日期
     * @return 本月的天数
     */
    public static int getActualMaxDaysOfMonth(Date date) {
        if (null == date) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定域最大天数
     *
     * @param date
     * @param dateField
     * @return
     */
    public static int getActualMaxDays(Date date, DateField dateField) {
        if (null == date) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        if (DateField.DAY_OF_YEAR.equals(dateField)) {
            return calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
        }
        if (DateField.DAY_OF_MONTH.equals(dateField)) {
            return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        }
        if (DateField.DAY_OF_WEEK.equals(dateField)) {
            return calendar.getActualMaximum(Calendar.DAY_OF_WEEK);
        }
        return 1;
    }

    /**
     * 获取当前时间至当天结束剩余的时间（秒）
     */
    public static Long getTodayLeftSeconds(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        long start = instance.getTimeInMillis();
        instance.add(DateField.DATE.getCode(), 1);
        instance.set(instance.get(DateField.YEAR.getCode()), instance.get(DateField.MONTH.getCode()), instance.get(DateField.DATE.getCode()), 0, 0, 0);
        Long end = instance.getTimeInMillis();
        return (end - start) / 1000;
    }

    /**
     * 两个日期，是否属于同一天
     *
     * @param date1 日期1
     * @param date2 日期2
     * @return true：属于同一个周；false：不属于同一个周
     */
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return false;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date1);
        int year = calendar.get(Calendar.YEAR);
        int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
        calendar.setTime(date2);
        return year == calendar.get(Calendar.YEAR) && dayOfYear == calendar.get(Calendar.DAY_OF_YEAR);
    }

    /**
     * 两个日期，是否属于同一周
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean isSameWeek(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return false;
        }
        String d1 = DateFormatUtil.format(date1, DatePattern.YYYY_WW);
        String d2 = DateFormatUtil.format(date2, DatePattern.YYYY_WW);
        return StringUtils.equals(d1, d2);
    }

    /**
     * 判断是否是周末
     *
     * @param date
     * @return
     */
    public static boolean isWeekend(Date date) {
        if (null == date) {
            return false;
        }
        Calendar calendar = initCalendar(date);
        return calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY;
    }

    /**
     * 判断是否是月末
     *
     * @param date
     * @return
     */
    public static boolean isLastMonthly(Date date) {
        if (null == date) {
            return false;
        }
        Calendar calendar = initCalendar(date);
        calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
        return calendar.get(Calendar.DAY_OF_MONTH) == 1;
    }

    /**
     * 判断是否年末
     *
     * @param date
     * @return
     */
    public static boolean isLastYearly(Date date) {
        if (null == date) {
            return false;
        }
        Calendar calendar = initCalendar(date);
        String format = DateFormatUtil.format(calendar.getTime(), DatePattern.MM_DD_EN);
        return StringUtils.equals(format, "12/31");
    }
}
